/*
 * Package il.ac.biu.cs.grossmm.impl.server
 * File RegistryServer.java
 * 
 * This is a part of presence service framework prototype. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package il.ac.biu.cs.grossmm.impl.server;

import il.ac.biu.cs.grossmm.api.server.Loader;
import il.ac.biu.cs.grossmm.api.server.Server;
import il.ac.biu.cs.grossmm.api.server.Service;
import il.ac.biu.cs.grossmm.api.server.ServiceManager;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;

public class RegistryServer extends Registry implements Service, Runnable,
	Server {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(RegistryServer.class);

	Registry registry = new Registry();

	protected final ServiceManager serviceManager;

	List<Object> startups = new ArrayList<Object>();

	List<Object> started = new LinkedList<Object>();

	private ServerState serverState;

	Thread starterThread;

	static {
		BasicConfigurator.configure();
	}

	public RegistryServer()
	{
		this(null);
	}

	public RegistryServer(Loader fallbackLoader)
	{
		super(fallbackLoader);
		serverState = NOT_RUNNING;

		WrapperImpl wrapper = new WrapperImpl(this);

		WrappedImpl wrapped = wrapper.wrap(this);

		try {
			wrapped.initialize();
		} catch (Exception e) {
			throw new Error(e);
		}

		this.serviceManager = wrapped.getServiceManager();

		register(Server.class, this);
	}

	public void addToStartup(Object hint)
	{
		startups.add(hint);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see il.ac.biu.cs.grossmm.impl.server.Server#start()
	 */
	public synchronized void start() throws Exception
	{
		start(serviceManager);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see il.ac.biu.cs.grossmm.impl.server.Server#stop()
	 */
	public void stop()
	{
		stop(null);
	}

	public synchronized void start(ServiceManager serviceManager)
	{
		if (serverState != NOT_RUNNING)
			throw new IllegalStateException(serverState.toString());

		logger.info("---------------------- STARTING SERVER---------------------");
		
		starterThread = Thread.currentThread();
		serverState = STARTING;
		notify();

		for (Object hint : startups) {
			logger.info("SERVER STARTING SERVICE BY HINT " + hint);
			try {
				serviceManager.requireService(hint);
			} catch (Exception e) {
				e.printStackTrace();
				serverState = STOPPING;
			}
			if (serverState == STOPPING) { // shutdown initiated
				logger.info("STARTUP TERMINATED BY SERVICE WITH HINT " + hint);
				stop();
				return;
			}

			started.add(0, hint);
			logger.info("STARTED SERVICE BY HINT " + hint);
		}
		
		logger.info("---------------------- SERVER STARTED ---------------------");

		new Thread(this).start();

		// wait until services terminate or shutdown initiated
		while (serverState != RUNNING)
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
				stop();
			}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see il.ac.biu.cs.grossmm.impl.server.Server#stop(java.lang.Exception)
	 */
	public synchronized void stop(Exception exception)
	{
		if (serverState == STARTING && starterThread == Thread.currentThread()) {
			serverState = STOPPING;
			return;
		}

		while (serverState == STARTING)
			try {
				wait();
			} catch (InterruptedException e) {
				System.exit(-1);
			}

		logger.info("--------------------- STOPPING SERVER ---------------------");

		serverState = STOPPING;
		notify();

		for (Object hint : started) {
			logger.info("STOPPING SERVICE BY HINT " + hint);
			serviceManager.releaseService(hint);
			logger.info("STOPPED SERVICE BY HINT " + hint);
		}

		serverState = NOT_RUNNING;
		logger.info("--------------------- SERVER STOPPED --------------------");
	}

	public void terminated(Object serviceHint, Exception e)
	{
		if (e != null) {
			logger.error("TERMINATED " + serviceHint + " ON EXCEPTION: "
				+ e.getMessage());
			serviceManager.terminated(e);
		}

		logger.error("SERVER IS DOWN");
	}

	public synchronized void run()
	{
		serverState = RUNNING;
		notify();

		try {
			while (serverState == RUNNING)
				wait();
		} catch (InterruptedException e) {
			stop();
		}
	}

	public ServerState getState()
	{
		return serverState;
	}

}
